﻿// 2023/4/18: 首个版本
// 2023/4/26: 改为使用skia绘制
// 2023/4/28: 处理tab字符
// 2023/9/22: 使用PassParent给ShowDialog传参
// 2023/11/24: 新增Clear方法
// 2024/1/25: 使用QueueRender
// 2024/1/26: 确保以较高频率刷新

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using ASEva;
using ASEva.Utility;
using ASEva.UIEto;
using Eto.Forms;
using SkiaSharp;

namespace CommonSource
{
    class DebuggerPanel : SkiaView
    {
        public DebuggerPanel(String moduleID) : base(moduleID, false)
        {
            this.moduleID = moduleID;

            var menu = this.SetContextMenuAsNew();

            itemClear = menu.AddButtonItem(chinese ? "清空" : "Clear");
            itemScroll = menu.AddCheckItem(chinese ? "自动滚动" : "Auto scroll");
            itemScroll.Checked = true;
            menu.AddSeparator();
            itemExport = menu.AddButtonItem(chinese ? "导出" : "Export");

            itemClear.Click += itemClear_Click;
            itemExport.Click += itemExport_Click;
            MouseDoubleClick += drawable_MouseDoubleClick;
            MouseWheel += drawable_MouseWheel;
            MouseMove += drawable_MouseMove;
            Render += drawable_Paint;
        }

        public void Clear()
        {
            if (bufferTable.ContainsKey(id))
            {
                bufferTable[id].Clear();
                target = -1;
            }
        }

        public void InputMessage(String msg, String id)
        {
            var rows = msg.Replace("\r\n", "\n").Split('\n');
            var okMsgs = new List<string>();
            foreach (var row in rows)
            {
                if (row.Length == 0) continue;
                okMsgs.Add(row.Replace("\t", "    "));
            }

            if (bufferTable.ContainsKey(id))
            {
                var targetBuffer = bufferTable[id];
                targetBuffer.AddRange(okMsgs);
                if (targetBuffer.Count > 11000)
                {
                    targetBuffer.RemoveRange(0, 1000);
                    if (this.id == id) target -= 1000;
                }
            }
            else bufferTable[id] = okMsgs.ToList();
        }

        public void InputMessages(Dictionary<String, List<String>> msgTable)
        {
            foreach (var pair in msgTable)
            {
                var id = pair.Key;
                var msgs = pair.Value;

                var okMsgs = new List<string>();
                foreach (var msg in msgs)
                {
                    var rows = msg.Replace("\r\n", "\n").Split('\n');
                    foreach (var row in rows)
                    {
                        if (row.Length == 0) continue;
                        okMsgs.Add(row.Replace("\t", "    "));
                    }
                }

                if (bufferTable.ContainsKey(id))
                {
                    var targetBuffer = bufferTable[id];
                    targetBuffer.AddRange(okMsgs);
                    if (targetBuffer.Count > 11000)
                    {
                        targetBuffer.RemoveRange(0, 1000);
                        if (this.id == id) target -= 1000;
                    }
                }
                else bufferTable[id] = okMsgs.ToList();
            }
        }

        public String[] GetIDList()
        {
            return bufferTable.Keys.ToArray();
        }

        public Dictionary<String, int> GetCountTable()
        {
            var table = new Dictionary<String, int>();
            foreach (var pair in bufferTable)
            {
                table[pair.Key] = pair.Value.Count;
            }
            return table;
        }

        public void SetTargetID(String targetID)
        {
            if (targetID == null) targetID = "";
            if (targetID != id)
            {
                id = targetID;
                target = -1;
            }
        }

        public void InvalidateDrawable()
        {
            if (DrawBeat.CallerBegin(this) || (DateTime.Now - lastQueueRenderTime).TotalMilliseconds > 100)
            {
                QueueRender();
                lastQueueRenderTime = DateTime.Now;
                DrawBeat.CallerEnd(this);
            }
        }

        private void exportMessages(String filePath)
        {
            var stream = File.Create(filePath);
            var writer = new StreamWriter(stream);

            if (bufferTable.ContainsKey(id))
            {
                foreach (var row in bufferTable[id])
                {
                    writer.WriteLine(row);
                }
            }

            writer.Close();
            stream.Close();
        }

        private void itemClear_Click(object sender, EventArgs e)
        {
            Clear();
        }

        private void itemExport_Click(object sender, EventArgs e)
        {
            var dialog = new SaveFileDialog();
            dialog.Title = chinese ? "导出文件选择" : "Please choose the file to export";
            dialog.Filters.Add(new FileFilter(chinese ? "文本文件 (*.txt)" : "Text file (*.txt)", ".txt"));
            if (dialog.ShowDialog(App.PassParent(this)) == DialogResult.Ok) exportMessages(dialog.FileName);
        }

        private void drawable_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            itemScroll.Checked = !itemScroll.Checked;
        }

        void drawable_MouseWheel(object sender, MouseEventArgs e)
        {
            itemScroll.Checked = false;
            if (e.Delta.Height > 0) target--;
            else target++;
            if (target < 0) target = 0;
        }

        private void doPaint(SKCanvas c)
        {
            c.Clear(SKColors.Gray);

            if (this.GetLogicalWidth() < 10 || this.GetLogicalHeight() < 10) return;

            int rowHeight = 16;
            int rowCount = (this.GetLogicalHeight() - 4) / rowHeight;

            List<String> msgs = null;
            int beginRow = 0;

            if (!bufferTable.ContainsKey(id)) return;

            var buffer = bufferTable[id];
            if (buffer.Count == 0) return;

            if (target < rowCount) target = rowCount - 1;
            if (itemScroll.Checked || target >= buffer.Count) target = buffer.Count - 1;

            beginRow = Math.Max(0, target - rowCount + 1);
            msgs = buffer.GetRange(beginRow, target - beginRow + 1);

            if (msgs == null || msgs.Count == 0) return;

            var font = c.GetDefaultFont(0.9f);
            for (int i = 0; i < msgs.Count; i++)
            {
                c.DrawString(msgs[i], font, SKColors.WhiteSmoke, TextAnchor.TopLeft, 4, 4 + i * rowHeight);
            }

            var rectBegin = (float)beginRow / buffer.Count * (this.GetLogicalHeight() - 2);
            var rectSize = (float)msgs.Count / buffer.Count * (this.GetLogicalHeight() - 2) + 2;

            c.DrawRect((float)this.GetLogicalWidth() - 8, rectBegin, 8, rectSize, new SKPaint{ Color = SKColors.WhiteSmoke });
        }

        private void drawable_Paint(object sender, SkiaRenderEventArgs e)
        {
            DrawBeat.CallbackBegin(this, moduleID);
            try { doPaint(e.Canvas); }
            catch (Exception) {}
            DrawBeat.CallbackEnd(this);
        }

        private void drawable_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Buttons == MouseButtons.Primary && bufferTable.ContainsKey(id))
            {
                itemScroll.Checked = false;
                target = (int)((float)e.GetLogicalPoint().Y / this.GetLogicalHeight() * bufferTable[id].Count);
            }
        }

        private bool chinese = Agency.GetAppLanguage() == "ch";

        private ButtonMenuItem itemClear, itemExport;
        private CheckMenuItem itemScroll;

        private DateTime lastQueueRenderTime;

        private Dictionary<String, List<String>> bufferTable = new Dictionary<String, List<String>>();
        private String id = "";
        private int target = -1;

        private String moduleID;
    }
}
